#include "znx_listeners_conf.h"
#include "comm/znx_yaml_parser.h"


static znx_bool_t znx_listeners_command_parser(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);


static znx_command_t    znx_listener_commands[] = {
    {
        ZNX_STRING("listener_addr"),
        znx_command_set_string,
        offsetof(znx_listener_conf_t, listener_addr)
    },

    {
        ZNX_STRING("backlog"),
        znx_command_set_int,
        offsetof(znx_listener_conf_t, backlog)
    },

    {
        ZNX_STRING("nodelay"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, nodelay)
    },

    {
        ZNX_STRING("quicack"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, quicack)
    },

    {
        ZNX_STRING("reuseport"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, reuseport)
    },

    {
        ZNX_STRING("reuseaddr"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, reuseaddr)
    },

    {
        ZNX_STRING("ipv6only"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, ipv6only)
    },

    {
        ZNX_STRING("protocol"),
        znx_command_set_string,
        offsetof(znx_listener_conf_t, protocol)
    },

    {
        ZNX_STRING("ssl"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, ssl)
    },

    {
        ZNX_STRING("http2"),
        znx_command_set_bool,
        offsetof(znx_listener_conf_t, http2)
    },

    znx_null_command
};


static znx_command_t    znx_listeners_commands[] = {
    {
        ZNX_STRING("listeners"),
        znx_listeners_command_parser,
        0
    },

    znx_null_command
};


static znx_bool_t
znx_listeners_command_parser(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    znx_listeners_conf_t    *listeners_conf = conf;
    size_t                  err_buf_len = command->name.len + 128;

    if (node->type != YAML_SEQUENCE_NODE) {
        err->data = znx_pool_malloc(pool, err_buf_len);
        err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
            "command %v value node is not sequence node", &command->name) - err->data);
        return ZNX_FALSE;
    }

    yaml_node_item_t        *item;
    yaml_node_t             *value_node;
    znx_listener_conf_t     *listener_conf;

    for (item = node->data.sequence.items.start;
        item < node->data.sequence.items.top;
        item++)
    {
        value_node = yaml_document_get_node(doc, *item);
        if (value_node->type != YAML_MAPPING_NODE) {
            err->data = znx_pool_malloc(pool, err_buf_len);
            err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
                "command %v found no map node element", &command->name) - err->data);
            return ZNX_FALSE;
        }

        listener_conf = znx_array_push(listeners_conf->array);
        listener_conf->listener_addr.data = NULL;
        listener_conf->listener_addr.len = 0;
        listener_conf->backlog = ZNX_INT_UNSET;
        listener_conf->nodelay = ZNX_BOOL_UNSET;
        listener_conf->quicack = ZNX_BOOL_UNSET;
        listener_conf->reuseport = ZNX_BOOL_UNSET;
        listener_conf->ipv6only = ZNX_BOOL_UNSET;
        listener_conf->protocol.data = NULL;
        listener_conf->protocol.len = 0;
        listener_conf->ssl = ZNX_BOOL_UNSET;
        listener_conf->http2 = ZNX_BOOL_UNSET;

        if (!znx_yaml_map_node_parser(listener_conf, pool, doc,
            value_node, znx_listener_commands, err))
        {
            return ZNX_FALSE;
        }
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_listener_conf_check_and_fix(znx_listeners_conf_t *conf,
    znx_listener_conf_t *listener_conf)
{
    if (listener_conf->listener_addr.len == 0) {
        ZNX_STR_SET(&conf->err, "found invalid listener_addr");
        return ZNX_FALSE;
    }

    int         port;
    znx_bool_t  ret;
    ret = znx_address_parser(listener_conf->listener_addr.data,
        listener_conf->listener_addr.len, &listener_conf->listener_address, &port);

    if (!ret || port <= 0) {
        size_t err_buf_size = listener_conf->listener_addr.len + 128;
        conf->err.data = znx_pool_malloc(conf->pool, err_buf_size);
        conf->err.len = (size_t)(znx_snprintf(conf->err.data,
            err_buf_size, "parse %v failed or no listene port",
            &listener_conf->listener_addr) - conf->err.data);
        return ZNX_FALSE;
    }

    if (listener_conf->backlog == ZNX_INT_UNSET) {
        listener_conf->backlog = 1024;
    }

    if (listener_conf->nodelay == ZNX_BOOL_UNSET) {
        listener_conf->nodelay = ZNX_TRUE;
    }

    if (listener_conf->quicack == ZNX_BOOL_UNSET) {
        listener_conf->quicack = ZNX_TRUE;
    }

    if (listener_conf->reuseport == ZNX_BOOL_UNSET) {
        listener_conf->reuseport = ZNX_FALSE;
    }

    if (listener_conf->reuseaddr == ZNX_BOOL_UNSET) {
        listener_conf->reuseaddr = ZNX_TRUE;
    }

    if (listener_conf->ipv6only == ZNX_BOOL_UNSET) {
        listener_conf->ipv6only = ZNX_FALSE;
    }

    if (listener_conf->protocol.len == 0) {
        ZNX_STR_SET(&conf->err, "found invalid protocol");
        return ZNX_FALSE;
    }

    if (listener_conf->ssl == ZNX_BOOL_UNSET) {
        listener_conf->ssl = ZNX_FALSE;
    }

    if (listener_conf->http2 == ZNX_BOOL_UNSET) {
        listener_conf->http2 = ZNX_FALSE;
    }

    return ZNX_TRUE;
}



znx_bool_t
znx_listeners_conf_parse(znx_listeners_conf_t *conf,
    const char *filepath)
{
    if (!znx_yaml_map_conf_parser(conf, conf->pool,
        filepath, znx_listeners_commands, &conf->err))
    {
        return ZNX_FALSE;
    }

    znx_listener_conf_t         *listener_conf;
    size_t                      i;

    listener_conf = conf->array->elts;
    for (i = 0; i < conf->array->nelts; i++) {
        if (!znx_listener_conf_check_and_fix(conf, listener_conf + i)) {
            return ZNX_FALSE;
        }
    }

    return ZNX_TRUE;
}